#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PDF书签处理器
功能：
1. 提取PDF图像层
2. 使用PaddleOCR识别文本
3. 根据规则生成一级和二级书签
4. 创建双层PDF书签结构
5. 支持批量处理多个PDF文件
6. 支持多线程并发处理
"""

import fitz
import cv2
import numpy as np
from paddleocr import PaddleOCR
import re
import os
import configparser
import threading
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import List, Dict, Tuple, Optional
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
from openpyxl.utils import get_column_letter

class PDFBookmarkProcessor:
    def __init__(self, pdf_path: str, config_output_dir: str = None):
        """
        初始化PDF书签处理器
        
        Args:
            pdf_path: PDF文件路径
            config_output_dir: 配置文件指定的输出目录，用于XLSX文件输出
        """
        self.pdf_path = pdf_path
        self.doc = fitz.open(pdf_path)
        self.ocr = PaddleOCR(
            det_model_name="ch_PP-OCRv4_server_det", rec_model_name="ch_PP-OCRv4_server_rec",
            use_textline_orientation=True,
            use_doc_orientation_classify=False,
            use_doc_unwarping=False,
            text_det_thresh=0.1,  # 二值化阈值
            text_det_box_thresh=0.3,  # 检测框阈值
            text_det_unclip_ratio=1.3,  # 检测框扩展比例
            text_det_limit_side_len=960,  # 检测图像的最大边长
            text_rec_score_thresh=0.6,
            text_recognition_batch_size=1,  # 识别批处理大小
        )
        self.pages_content = []
        self.bookmarks = []
        
        # 创建输出目录
        self.base_name = os.path.splitext(os.path.basename(pdf_path))[0]
        self.output_dir = f"{self.base_name}_output"
        self.images_dir = os.path.join(self.output_dir, "extracted_images")
        self.ocr_dir = os.path.join(self.output_dir, "ocr_visualization")
        self.text_dir = os.path.join(self.output_dir, "extracted_text")
        self.prefix_regions_dir = os.path.join(self.output_dir, "prefix_regions")  # 前缀区域图像目录
        
        # 设置配置文件输出目录（用于XLSX文件）
        self.config_output_dir = config_output_dir if config_output_dir else "bookmark_output"
        
        # 创建所有输出目录
        for dir_path in [self.output_dir, self.images_dir, self.ocr_dir, self.text_dir, self.prefix_regions_dir, self.config_output_dir]:
            if not os.path.exists(dir_path):
                os.makedirs(dir_path)
                print(f"创建目录: {dir_path}")
        
    def extract_page_images(self, save_images: bool = True) -> List[np.ndarray]:
        """
        提取PDF每页的图像和文本层
        
        Args:
            save_images: 是否保存图像到文件
            
        Returns:
            List[np.ndarray]: 每页的图像数组
        """
        print("开始提取PDF页面图像和文本层...")
        images = []
        
        # 确保图像和文本保存目录存在
        if save_images:
            images_dir = self.images_dir
            text_dir = self.text_dir
            
            for dir_path in [images_dir, text_dir]:
                if not os.path.exists(dir_path):
                    os.makedirs(dir_path, exist_ok=True)
                    print(f"创建保存目录: {dir_path}")
            print(f"图像将保存到: {images_dir}")
            print(f"文本将保存到: {text_dir}")
        
        for page_num in range(len(self.doc)):
            try:
                page = self.doc[page_num]
                
                # 1. 提取页面图像
                mat = fitz.Matrix(2.0, 2.0)  # 2倍缩放提高OCR精度
                pix = page.get_pixmap(matrix=mat)
                img_data = pix.tobytes("png")
                
                # 转换为OpenCV格式
                nparr = np.frombuffer(img_data, np.uint8)
                img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
                images.append(img)
                
                # 保存图像到文件
                if save_images:
                    image_path = os.path.join(images_dir, f"page_{page_num+1:03d}.png")
                    try:
                        # 使用PIL保存图像，更可靠
                        from PIL import Image
                        pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
                        pil_img.save(image_path, 'PNG')
                        print(f"✓ 保存第 {page_num+1} 页图像: {image_path}")
                    except Exception as save_error:
                        print(f"✗ 保存第 {page_num+1} 页图像失败: {image_path}, 错误: {save_error}")
                        # 尝试使用OpenCV作为备选方案
                        try:
                            success = cv2.imwrite(image_path, img)
                            if success:
                                print(f"✓ 使用OpenCV保存第 {page_num+1} 页图像: {image_path}")
                            else:
                                print(f"✗ OpenCV也保存失败: {image_path}")
                        except Exception as cv_error:
                            print(f"✗ OpenCV保存也失败: {cv_error}")
                
                # 2. 提取页面文本层
                if save_images:
                    self.extract_page_text_layer(page, page_num, text_dir)
                        
            except Exception as e:
                print(f"✗ 处理第 {page_num+1} 页时出错: {e}")
                continue
            
        print(f"成功提取 {len(images)} 页图像")
        if save_images:
            # 检查实际保存的文件数量
            saved_image_files = [f for f in os.listdir(images_dir) if f.endswith('.png')]
            saved_text_files = [f for f in os.listdir(text_dir) if f.endswith('.txt')]
            print(f"实际保存了 {len(saved_image_files)} 个图像文件到: {images_dir}")
            print(f"实际保存了 {len(saved_text_files)} 个文本文件到: {text_dir}")
        return images
    
    def extract_page_text_layer(self, page, page_num: int, output_dir: str):
        """
        提取页面的文本层并保存
        
        Args:
            page: PDF页面对象
            page_num: 页面编号
            output_dir: 输出目录
        """
        try:
            # 提取页面文本
            text = page.get_text()
            
            # 保存页面文本
            text_file = os.path.join(output_dir, f"page_{page_num+1:03d}_text.txt")
            with open(text_file, 'w', encoding='utf-8') as f:
                f.write(text)
            print(f"✓ 保存第 {page_num+1} 页文本: {text_file}")
            
            # 提取文本块信息（更详细的文本信息）
            text_dict = page.get_text("dict")
            text_blocks = []
            
            for block in text_dict["blocks"]:
                if "lines" in block:  # 文本块
                    block_text = ""
                    for line in block["lines"]:
                        line_text = ""
                        for span in line["spans"]:
                            line_text += span["text"]
                        block_text += line_text + "\n"
                    if block_text.strip():
                        text_blocks.append(block_text.strip())
            
            # 保存文本块信息
            if text_blocks:
                blocks_file = os.path.join(output_dir, f"page_{page_num+1:03d}_text_blocks.txt")
                with open(blocks_file, 'w', encoding='utf-8') as f:
                    for i, block in enumerate(text_blocks, 1):
                        f.write(f"=== 文本块 {i} ===\n")
                        f.write(block)
                        f.write(f"\n\n")
                print(f"✓ 保存第 {page_num+1} 页文本块: {blocks_file}")
            
        except Exception as e:
            print(f"✗ 提取第 {page_num+1} 页文本层失败: {e}")
    
    def ocr_page(self, image: np.ndarray, page_num: int = 0, save_ocr_visualization: bool = True) -> List[Dict]:
        """
        对单页图像进行OCR识别
        
        Args:
            image: 页面图像
            page_num: 页面编号
            save_ocr_visualization: 是否保存OCR可视化结果
            
        Returns:
            List[Dict]: OCR识别结果，包含文本、位置、置信度等信息
        """
        try:
            result = self.ocr.ocr(image, cls=True)
            
            if not result or not result[0]:
                return []
                
            ocr_results = []
            for line in result[0]:
                if line and len(line) >= 2:
                    # 提取文本和位置信息
                    bbox = line[0]  # 边界框坐标
                    text_info = line[1]  # (文本, 置信度)
                    
                    if not text_info or len(text_info) < 2:
                        continue
                    
                    # 计算文本区域的高度（用于判断字体大小）
                    # 考虑文字方向，避免竖向文字被误认为很高
                    bbox_height = max(bbox[2][1] - bbox[0][1], bbox[3][1] - bbox[1][1])
                    bbox_width = max(bbox[1][0] - bbox[0][0], bbox[2][0] - bbox[3][0])
                    
                    # 如果宽度明显大于高度，说明是横向文字，使用高度
                    # 如果高度明显大于宽度，说明是竖向文字，使用宽度作为"有效高度"
                    if bbox_width > bbox_height * 1.5:  # 横向文字
                        height = bbox_height
                    elif bbox_height > bbox_width * 1.5:  # 竖向文字
                        height = bbox_width  # 用宽度作为有效高度
                    else:  # 接近正方形，可能是单个字符
                        height = min(bbox_height, bbox_width)
                    
                    # 清理OCR识别文本
                    original_text = text_info[0].strip()
                    cleaned_text = self.clean_ocr_text(original_text)
                    
                    ocr_results.append({
                        'text': cleaned_text,
                        'original_text': original_text,  # 保存原始文本用于调试
                        'confidence': text_info[1],
                        'bbox': bbox,
                        'height': height,
                        'width': bbox_width,
                        'bbox_height': bbox_height,  # 原始高度
                        'center_y': (bbox[0][1] + bbox[2][1]) / 2
                    })
            
            # 保存OCR可视化结果
            if save_ocr_visualization and ocr_results:
                self.save_ocr_visualization(image, ocr_results, page_num)
            
            return ocr_results
            
        except Exception as e:
            print(f"警告：OCR识别页面时出现错误: {e}")
            return []
    
    def save_ocr_visualization(self, image: np.ndarray, ocr_results: List[Dict], page_num: int):
        """
        保存OCR识别结果的可视化图像
        
        Args:
            image: 原始图像
            ocr_results: OCR识别结果
            page_num: 页面编号
        """
        try:
            # 检查OCR结果是否为空
            if not ocr_results:
                print(f"第 {page_num+1} 页没有OCR结果，跳过可视化保存")
                return
            
            print(f"开始保存第 {page_num+1} 页OCR可视化，共 {len(ocr_results)} 个文本区域")
            
            # 创建可视化图像
            vis_image = image.copy()
            
            # 确保OCR结果目录存在
            ocr_dir = self.ocr_dir
            if not os.path.exists(ocr_dir):
                os.makedirs(ocr_dir, exist_ok=True)
                print(f"创建OCR可视化目录: {ocr_dir}")
            
            # 在图像上绘制边界框和文本
            for i, result in enumerate(ocr_results):
                try:
                    bbox = result['bbox']
                    text = result['text']
                    confidence = result['confidence']
                    
                    # 绘制边界框
                    points = np.array(bbox, dtype=np.int32)
                    cv2.polylines(vis_image, [points], True, (0, 255, 0), 2)
                    
                    # 添加文本标签
                    x, y = int(bbox[0][0]), int(bbox[0][1])
                    label = f"{text[:20]}...({confidence:.2f})" if len(text) > 20 else f"{text}({confidence:.2f})"
                    cv2.putText(vis_image, label, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
                except Exception as draw_error:
                    print(f"绘制第 {i+1} 个文本区域时出错: {draw_error}")
                    continue
            
            # 保存可视化图像
            vis_path = os.path.join(ocr_dir, f"page_{page_num+1:03d}_ocr.png")
            
            # 尝试使用PIL保存，更可靠
            try:
                from PIL import Image
                pil_img = Image.fromarray(cv2.cvtColor(vis_image, cv2.COLOR_BGR2RGB))
                pil_img.save(vis_path, 'PNG')
                print(f"✓ 保存OCR可视化结果: {vis_path}")
            except Exception as pil_error:
                print(f"PIL保存失败: {pil_error}，尝试使用OpenCV")
                # 使用OpenCV作为备选方案
                success = cv2.imwrite(vis_path, vis_image)
                if success:
                    print(f"✓ 使用OpenCV保存OCR可视化结果: {vis_path}")
                else:
                    print(f"✗ OpenCV保存OCR可视化失败: {vis_path}")
            
        except Exception as e:
            print(f"保存OCR可视化时出现错误: {e}")
            import traceback
            traceback.print_exc()
    
    def save_page_text(self, page_num: int, ocr_results: List[Dict]):
        """
        保存页面识别到的文本到txt文件
        
        Args:
            page_num: 页面编号
            ocr_results: OCR识别结果
        """
        try:
            text_file = os.path.join(self.text_dir, f"page_{page_num+1:03d}_text.txt")
            
            with open(text_file, 'w', encoding='utf-8') as f:
                f.write(f"第 {page_num+1} 页识别到的文本内容\n")
                f.write("=" * 50 + "\n\n")
                
                if not ocr_results:
                    f.write("未识别到任何文本\n")
                else:
                    for i, result in enumerate(ocr_results, 1):
                        f.write(f"{i:3d}. 文本: {result['text']}\n")
                        f.write(f"     置信度: {result['confidence']:.3f}\n")
                        f.write(f"     高度: {result['height']:.1f}px\n")
                        f.write(f"     位置: Y={result['center_y']:.1f}\n")
                        f.write("-" * 30 + "\n")
            
            print(f"保存第 {page_num+1} 页文本: {text_file}")
            
        except Exception as e:
            print(f"保存页面文本时出现错误: {e}")
    
    def save_all_texts(self):
        """保存所有页面的文本内容到一个总文件"""
        try:
            all_text_file = os.path.join(self.text_dir, "all_pages_text.txt")
            
            with open(all_text_file, 'w', encoding='utf-8') as f:
                f.write(f"PDF文件: {self.pdf_path}\n")
                f.write(f"总页数: {len(self.pages_content)}\n")
                f.write("=" * 80 + "\n\n")
                
                for page_content in self.pages_content:
                    page_num = page_content['page_num']
                    ocr_results = page_content['ocr_results']
                    
                    f.write(f"第 {page_num+1} 页\n")
                    f.write("-" * 40 + "\n")
                    
                    if not ocr_results:
                        f.write("未识别到任何文本\n\n")
                    else:
                        for result in ocr_results:
                            f.write(f"{result['text']}\n")
                        f.write("\n")
            
            print(f"保存所有页面文本: {all_text_file}")
            
        except Exception as e:
            print(f"保存所有文本时出现错误: {e}")
    
    def save_bookmarks(self):
        """保存书签信息到txt文件"""
        try:
            bookmarks_file = os.path.join(self.text_dir, "bookmarks.txt")
            
            with open(bookmarks_file, 'w', encoding='utf-8') as f:
                f.write(f"PDF文件: {self.pdf_path}\n")
                f.write(f"书签生成时间: {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write("=" * 80 + "\n\n")
                
                level1_count = len([b for b in self.bookmarks if b['level'] == 1])
                level2_count = len([b for b in self.bookmarks if b['level'] == 2])
                
                f.write(f"书签统计:\n")
                f.write(f"- 一级书签: {level1_count} 个\n")
                f.write(f"- 二级书签: {level2_count} 个\n")
                f.write(f"- 总计: {len(self.bookmarks)} 个\n\n")
                
                f.write("书签详情:\n")
                f.write("-" * 50 + "\n")
                
                current_level1 = None
                for bookmark in self.bookmarks:
                    if bookmark['level'] == 1:
                        current_level1 = bookmark['title']
                        f.write(f"\n【一级书签】\n")
                        f.write(f"标题: {bookmark['title']}\n")
                        f.write(f"页码: {bookmark['page'] + 1}\n")
                    elif bookmark['level'] == 2:
                        f.write(f"  └─【二级书签】\n")
                        f.write(f"     标题: {bookmark['title']}\n")
                        f.write(f"     页码: {bookmark['page'] + 1}\n")
                        f.write(f"     父级: {bookmark['parent']}\n")
            
            print(f"保存书签信息: {bookmarks_file}")
            
        except Exception as e:
            print(f"保存书签信息时出现错误: {e}")
    
    def generate_xlsx_table(self, output_path: str = None):
        """
        生成退役军人档案目录XLSX表格文件
        
        Args:
            output_path: 输出XLSX文件路径，默认为配置文件指定的输出目录下的原文件名.xlsx
        """
        if output_path is None:
            # 获取原文件名（不包含路径和扩展名）
            original_filename = os.path.splitext(os.path.basename(self.pdf_path))[0]
            output_path = os.path.join(self.config_output_dir, f"{original_filename}.xlsx")
        
        try:
            # 创建工作簿
            wb = Workbook()
            ws = wb.active
            ws.title = "退役军人档案目录"
            
            # 设置表格标题
            ws.merge_cells('A1:G1')
            ws['A1'] = "退役军人档案目录"
            ws['A1'].font = Font(name='宋体', size=16, bold=True)
            ws['A1'].alignment = Alignment(horizontal='center', vertical='center')
            
            # 设置表头
            headers = ['序号', '材料名称', '材料形成时间', '', '', '页码', '备注']
            sub_headers = ['', '', '年', '月', '日', '', '']
            
            # 第二行：主表头
            for col_num, header in enumerate(headers, 1):
                cell = ws.cell(row=2, column=col_num, value=header)
                cell.font = Font(name='宋体', size=12, bold=True)
                cell.alignment = Alignment(horizontal='center', vertical='center')
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )
                cell.fill = PatternFill(start_color='F0F0F0', end_color='F0F0F0', fill_type='solid')
            
            # 第三行：子表头
            for col_num, sub_header in enumerate(sub_headers, 1):
                cell = ws.cell(row=3, column=col_num, value=sub_header)
                cell.font = Font(name='宋体', size=12, bold=True)
                cell.alignment = Alignment(horizontal='center', vertical='center')
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )
                cell.fill = PatternFill(start_color='F0F0F0', end_color='F0F0F0', fill_type='solid')
            
            # 合并表头单元格
            ws.merge_cells('A2:A3')  # 序号
            ws.merge_cells('B2:B3')  # 材料名称
            ws.merge_cells('C2:E2')  # 材料形成时间
            ws.merge_cells('F2:F3')  # 页码
            ws.merge_cells('G2:G3')  # 备注
            
            # 填充数据
            current_level1_title = None
            level1_counter = 0
            row_num = 4
            
            # 处理书签数据
            for bookmark in self.bookmarks:
                if bookmark['level'] == 1:
                    # 一级书签作为分类标题
                    current_level1_title = bookmark['title']
                    level1_counter += 1
                    
                    # 添加一级分类行
                    ws.cell(row=row_num, column=1, value=self.convert_to_chinese_number(level1_counter))
                    ws.cell(row=row_num, column=2, value=current_level1_title)
                    ws.cell(row=row_num, column=3, value='')  # 年
                    ws.cell(row=row_num, column=4, value='')  # 月
                    ws.cell(row=row_num, column=5, value='')  # 日
                    ws.cell(row=row_num, column=6, value='')  # 页码
                    ws.cell(row=row_num, column=7, value='')  # 备注
                    
                    # 设置一级分类行的样式
                    for col in range(1, 8):
                        cell = ws.cell(row=row_num, column=col)
                        cell.font = Font(name='宋体', size=11, bold=True)
                        cell.alignment = Alignment(horizontal='center', vertical='center')
                        cell.border = Border(
                            left=Side(style='thin'),
                            right=Side(style='thin'),
                            top=Side(style='thin'),
                            bottom=Side(style='thin')
                        )
                        cell.fill = PatternFill(start_color='E6F3FF', end_color='E6F3FF', fill_type='solid')
                    
                    row_num += 1
                    
                elif bookmark['level'] == 2:
                    # 二级书签作为具体材料
                    # 解析完整的多级前缀（如"2-1"、"3-1-1"、"4-2-3-1"）
                    prefix_match = re.search(r'^(\d+(?:-\d+)+)', bookmark['title'])
                    if prefix_match:
                        sequence_num = prefix_match.group(1)  # 直接使用完整的前缀作为序号
                        
                        # 提取材料名称（去掉前缀和后缀数字）
                        material_name = re.sub(r'^\d+(?:-\d+)+', '', bookmark['title'])  # 匹配多级前缀
                        material_name = re.sub(r'\d+$', '', material_name).strip()
                        
                        # 年月日列保持空白
                        year, month, day = '', '', ''
                    else:
                        sequence_num = bookmark['title'][:10] if len(bookmark['title']) > 10 else bookmark['title']
                        material_name = bookmark['title']
                        year, month, day = '', '', ''
                    
                    # 填充行数据
                    ws.cell(row=row_num, column=1, value=sequence_num)
                    ws.cell(row=row_num, column=2, value=material_name)
                    ws.cell(row=row_num, column=3, value=year)
                    ws.cell(row=row_num, column=4, value=month)
                    ws.cell(row=row_num, column=5, value=day)
                    ws.cell(row=row_num, column=6, value=str(bookmark['page'] + 1))  # 页码从1开始
                    ws.cell(row=row_num, column=7, value='')  # 备注留空
                    
                    # 设置二级材料行的样式
                    for col in range(1, 8):
                        cell = ws.cell(row=row_num, column=col)
                        cell.font = Font(name='宋体', size=10)
                        cell.alignment = Alignment(horizontal='center', vertical='center')
                        cell.border = Border(
                            left=Side(style='thin'),
                            right=Side(style='thin'),
                            top=Side(style='thin'),
                            bottom=Side(style='thin')
                        )
                    
                    row_num += 1
            
            # 设置列宽
            column_widths = {
                'A': 8,   # 序号
                'B': 40,  # 材料名称（增加宽度以完全显示文字）
                'C': 8,   # 年
                'D': 6,   # 月
                'E': 6,   # 日
                'F': 8,   # 页码
                'G': 12   # 备注
            }
            
            for column, width in column_widths.items():
                ws.column_dimensions[column].width = width
            
            # 设置行高
            ws.row_dimensions[1].height = 25  # 标题行
            ws.row_dimensions[2].height = 20  # 表头行
            ws.row_dimensions[3].height = 20  # 子表头行
            
            # 保存文件
            wb.save(output_path)
            print(f"✓ XLSX表格已生成: {output_path}")
            
        except Exception as e:
            print(f"✗ 生成XLSX表格时出现错误: {e}")
            import traceback
            traceback.print_exc()
    
    def convert_to_chinese_number(self, num: int) -> str:
        """
        将阿拉伯数字转换为中文数字
        
        Args:
            num: 阿拉伯数字
            
        Returns:
            str: 中文数字
        """
        chinese_nums = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
        if num <= 10:
            return chinese_nums[num]
        elif num < 20:
            return '十' + chinese_nums[num - 10]
        else:
            return str(num)  # 超过20的数字直接返回阿拉伯数字
    
    
    def process_all_pages(self):
        """处理所有页面，提取OCR结果"""
        print("开始OCR识别所有页面...")
        images = self.extract_page_images()
        total_pages = len(images)
        
        for i, image in enumerate(images):
            print(f"正在处理第 {i+1}/{total_pages} 页...")
            try:
                # 先进行全图OCR识别
                ocr_results = self.ocr_page(image, page_num=i)
                
                # 检查是否有一级书签
                temp_page_content = {
                    'page_num': i,
                    'ocr_results': ocr_results
                }
                level1_bookmark = self.find_level1_bookmark(temp_page_content)
                
                prefix_ocr_results = []
                if not level1_bookmark:
                    # 如果没有一级书签，进行左上角区域OCR识别（用于前缀识别）
                    print(f"第 {i+1} 页未发现一级书签，进行左上角区域前缀识别...")
                    prefix_ocr_results = self.ocr_prefix_region(image, page_num=i)
                else:
                    print(f"第 {i+1} 页发现一级书签，跳过前缀识别")
                
                self.pages_content.append({
                    'page_num': i,
                    'ocr_results': ocr_results,
                    'prefix_ocr_results': prefix_ocr_results,  # 添加左上角区域OCR结果
                    'texts': [r['text'] for r in ocr_results if r.get('text', '').strip()]
                })
                print(f"第 {i+1} 页识别到 {len(ocr_results)} 个文本区域，左上角区域 {len(prefix_ocr_results)} 个文本区域")
                
                # 保存每页的文本内容
                self.save_page_text(i, ocr_results)
                
            except Exception as e:
                print(f"错误：处理第 {i+1} 页时出现错误: {e}")
                self.pages_content.append({
                    'page_num': i,
                    'ocr_results': [],
                    'texts': []
                })
                # 即使出错也保存空的文本文件
                self.save_page_text(i, [])
        
        print("OCR识别完成")
    
    def find_level1_bookmark(self, page_content: Dict) -> Optional[str]:
        """
        识别一级书签（只有符合"第X类XXXX"格式才是一级书签，但内容使用全部文本组合）
        
        Args:
            page_content: 页面内容
            
        Returns:
            Optional[str]: 一级书签文本，如果未找到则返回None
        """
        ocr_results = page_content['ocr_results']
        if not ocr_results:
            return None
        
        # 获取第一行的文本（Y坐标最小的）
        min_y = min(r['center_y'] for r in ocr_results)
        first_line_threshold = min_y + 50  # 允许50像素的误差
        
        first_line_texts = []
        for result in ocr_results:
            if result['center_y'] <= first_line_threshold:
                first_line_texts.append(result)
        
        # 在第一行中查找"第X类XXXX"格式
        pattern = r'第[一二三四五六七八九十\d]+类[^\s]*'
        has_level1_pattern = False
        
        for result in first_line_texts:
            text = result['text'].strip()
            match = re.search(pattern, text)
            if match:
                has_level1_pattern = True
                break
        
        # 只有符合"第X类XXXX"格式才返回一级书签
        if not has_level1_pattern:
            return None
        
        # 如果符合格式，则组合当前页的所有文本作为书签内容
        all_texts = []
        for result in ocr_results:
            text = result['text'].strip()
            if text and len(text) >= 1:  # 包含所有非空文本
                all_texts.append(text)
        
        if not all_texts:
            return None
        
        # 将所有文本组合成一个字符串
        combined_text = ' '.join(all_texts)
        
        # 清理文本，去除多余的空格
        combined_text = re.sub(r'\s+', ' ', combined_text).strip()
        
        return combined_text if combined_text else None
    
    def get_level1_index(self, page_content: Dict) -> int:
        """
        获取当前页面所属的一级书签索引
        
        Args:
            page_content: 当前页面内容
            
        Returns:
            int: 一级书签索引（从1开始）
        """
        page_num = page_content['page_num']
        
        # 查找当前页之前最近的一级书签
        for i in range(page_num, -1, -1):  # 从当前页往前查找
            if i < len(self.pages_content):
                page = self.pages_content[i]
                level1_bookmark = self.find_level1_bookmark(page)
            if level1_bookmark:
                # 从一级书签标题中提取数字
                extracted_number = self.extract_number_from_title(level1_bookmark)
                if extracted_number:
                    return extracted_number
        
        # 如果没有找到一级书签，返回1
        return 1
    
    def contains_keyword(self, text: str) -> bool:
        """
        检查文本是否包含关键词
        
        Args:
            text: 要检查的文本
            
        Returns:
            bool: True表示包含关键词，False表示不包含
        """
        keywords = ["登记表", "（登记）表", "检查表", "鉴定表", "报告表", "（报告）表"]
        
        for keyword in keywords:
            if keyword in text:
                return True
        return False

    def validate_prefix(self, prefix: str, current_level1_index: int) -> bool:
        """
        验证前缀是否与当前一级书签匹配
        
        Args:
            prefix: 识别到的前缀，如"2-1"、"10-1"
            current_level1_index: 当前一级书签的索引，如2表示"第二类"
            
        Returns:
            bool: True表示前缀有效，False表示前缀无效
        """
        if not prefix:
            return False
        
        try:
            # 提取前缀的第一个数字
            first_number = int(prefix.split('-')[0])
            
            # 检查第一个数字是否与当前一级书签匹配
            if first_number == current_level1_index:
                print(f"✓ 前缀验证通过: '{prefix}' 的第一个数字 {first_number} 与当前一级书签索引 {current_level1_index} 匹配")
                return True
            else:
                print(f"✗ 前缀验证失败: '{prefix}' 的第一个数字 {first_number} 与当前一级书签索引 {current_level1_index} 不匹配，视为误识别")
                return False
                
        except (ValueError, IndexError) as e:
            print(f"✗ 前缀验证错误: 无法解析前缀 '{prefix}', 错误: {e}")
            return False

    def extract_number_from_title(self, title: str) -> int:
        """
        从一级书签标题中提取数字
        
        Args:
            title: 一级书签标题，如"第五类XXX"
            
        Returns:
            int: 提取的数字，如果未找到则返回None
        """
        import re
        
        # 中文数字到阿拉伯数字的映射
        chinese_to_arabic = {
            '一': 1, '二': 2, '三': 3, '四': 4, '五': 5,
            '六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
            '十一': 11, '十二': 12, '十三': 13, '十四': 14, '十五': 15,
            '十六': 16, '十七': 17, '十八': 18, '十九': 19, '二十': 20
        }
        
        # 匹配"第X类"格式
        pattern = r'第([一二三四五六七八九十]+)类'
        match = re.search(pattern, title)
        
        if match:
            chinese_num = match.group(1)
            # 先尝试直接匹配
            if chinese_num in chinese_to_arabic:
                return chinese_to_arabic[chinese_num]
            
            # 处理"十X"格式（如"十一"、"十二"等）
            if chinese_num.startswith('十') and len(chinese_num) > 1:
                remainder = chinese_num[1:]
                if remainder in chinese_to_arabic:
                    return 10 + chinese_to_arabic[remainder]
        
        # 如果没有匹配到中文数字，尝试匹配阿拉伯数字
        pattern = r'第(\d+)类'
        match = re.search(pattern, title)
        if match:
            return int(match.group(1))
        
        return None
    
    def get_largest_text_from_page(self, page_content: Dict) -> Optional[str]:
        """
        获取页面中字体最大的文本（只识别上半部分）
        
        Args:
            page_content: 页面内容
            
        Returns:
            Optional[str]: 字体最大的文本，如果没有则返回None
        """
        ocr_results = page_content['ocr_results']
        if not ocr_results:
            return None
        
        # 计算上半部分阈值
        min_y = min(r['center_y'] for r in ocr_results)
        max_y = max(r['center_y'] for r in ocr_results)
        top_threshold = min_y + (max_y - min_y) / 2  # 上半部分的中点
        
        # 计算页面宽度阈值 - 跳过左边X坐标大于二分之一宽度的文本块
        min_x = min(min(r['bbox'][0][0], r['bbox'][2][0]) for r in ocr_results)
        max_x = max(max(r['bbox'][1][0], r['bbox'][3][0]) for r in ocr_results)
        # 计算二分之一宽度
        half_width = min_x + (max_x - min_x) / 2  # 二分之一宽度点
        
        print(f"只对页面上半部分进行标题识别，跳过左边X坐标大于二分之一宽度的文本块 (Y坐标范围: {min_y:.1f} - {top_threshold:.1f}, 跳过X坐标 > {half_width:.1f} 的文本)")
        
        # 找到上半部分且左边X坐标小于等于二分之一宽度的字体最大的文本（高度最大的）
        upper_half_results = [r for r in ocr_results if r['center_y'] <= top_threshold and min(r['bbox'][0][0], r['bbox'][2][0]) <= half_width]
        if not upper_half_results:
            return None
            
        max_height = max(r['height'] for r in upper_half_results)
        largest_texts = []
        
        for result in upper_half_results:
            if result['height'] >= max_height * 0.8:  # 使用0.8的阈值
                text = result['text'].strip()
                confidence = result['confidence']
                text_type = result.get('text_type', 'printed')  # 获取文本类型
                is_handwritten = text_type == 'handwritten'  # 检查是否为手写文本
                high_confidence = confidence >= 0.9  # 置信度必须≥0.9
                
                # 过滤掉纯数字、符号、过短的文本，以及手写文本和低置信度文本
                if (len(text) > 2 and 
                    not re.match(r'^[\d\-\s\.、]+$', text) and
                    not re.match(r'^[^\u4e00-\u9fff]*$', text) and
                    not is_handwritten and  # 排除手写文本
                    high_confidence):  # 排除低置信度文本
                    largest_texts.append(text)
        
        if not largest_texts:
            return None
        
        # 选择最短的文本（最简洁的）
        return min(largest_texts, key=len)
    
    def extract_top_left_quarter(self, image: np.ndarray) -> np.ndarray:
        """
        提取图像的左上角1/4区域
        
        Args:
            image: 原始图像
            
        Returns:
            np.ndarray: 左上角1/4区域的图像
        """
        height, width = image.shape[:2]
        # 提取左上角1/4区域
        top_left_quarter = image[0:height//2, 0:width//2]
        return top_left_quarter
    
    def extract_top_left_quarter_width_twelfth_height(self, image: np.ndarray) -> np.ndarray:
        """
        提取图像的左上角区域（宽度1/4，高度1/12）
        
        Args:
            image: 原始图像
            
        Returns:
            np.ndarray: 左上角区域（宽度1/4，高度1/12）的图像
        """
        height, width = image.shape[:2]
        # 提取左上角区域：宽度1/4，高度1/12
        top_left_region = image[0:height//12, 0:width//4]
        return top_left_region
    
    def save_cropped_image(self, cropped_image: np.ndarray, page_num: int, region_type: str) -> str:
        """
        保存切割的图像到本地
        
        Args:
            cropped_image: 切割后的图像
            page_num: 页面编号
            region_type: 区域类型（如"quarter", "quarter_twelfth"）
            
        Returns:
            str: 保存的文件路径
        """
        try:
            # 确保前缀区域目录存在
            if not os.path.exists(self.prefix_regions_dir):
                os.makedirs(self.prefix_regions_dir, exist_ok=True)
            
            # 生成文件名
            filename = f"page_{page_num+1:03d}_{region_type}.png"
            file_path = os.path.join(self.prefix_regions_dir, filename)
            
            # 使用PIL保存图像，更可靠
            from PIL import Image
            pil_img = Image.fromarray(cv2.cvtColor(cropped_image, cv2.COLOR_BGR2RGB))
            pil_img.save(file_path, 'PNG')
            print(f"✓ 保存切割图像: {file_path}")
            return file_path
            
        except Exception as e:
            print(f"✗ 保存切割图像失败: {e}")
            # 尝试使用OpenCV作为备选方案
            try:
                filename = f"page_{page_num+1:03d}_{region_type}.png"
                file_path = os.path.join(self.prefix_regions_dir, filename)
                success = cv2.imwrite(file_path, cropped_image)
                if success:
                    print(f"✓ 使用OpenCV保存切割图像: {file_path}")
                    return file_path
                else:
                    print(f"✗ OpenCV保存切割图像失败: {file_path}")
                    return None
            except Exception as cv_error:
                print(f"✗ OpenCV保存切割图像也失败: {cv_error}")
                return None
    
    def save_prefix_region_text(self, page_num: int, prefix_ocr_results: List[Dict], strategy: str):
        """
        保存前缀区域识别到的文本到txt文件
        
        Args:
            page_num: 页面编号
            prefix_ocr_results: 前缀区域OCR识别结果
            strategy: 策略名称（如"strategy2"）
        """
        try:
            text_file = os.path.join(self.prefix_regions_dir, f"page_{page_num+1:03d}_{strategy}_text.txt")
            
            with open(text_file, 'w', encoding='utf-8') as f:
                f.write(f"第 {page_num+1} 页{strategy}识别到的文本内容\n")
                f.write("=" * 50 + "\n\n")
                
                if not prefix_ocr_results:
                    f.write("未识别到任何文本\n")
                else:
                    for i, result in enumerate(prefix_ocr_results, 1):
                        f.write(f"{i:3d}. 文本: {result['text']}\n")
                        f.write(f"     原始文本: {result.get('original_text', result['text'])}\n")
                        f.write(f"     置信度: {result['confidence']:.3f}\n")
                        f.write(f"     高度: {result['height']:.1f}px\n")
                        f.write(f"     宽度: {result['width']:.1f}px\n")
                        f.write(f"     位置: Y={result['center_y']:.1f}\n")
                        f.write("-" * 30 + "\n")
            
            print(f"✓ 保存{strategy}文本: {text_file}")
            
        except Exception as e:
            print(f"✗ 保存{strategy}文本时出现错误: {e}")
    
    def clean_ocr_text(self, text: str) -> str:
        """
        清理OCR识别文本，处理常见的误识别情况
        
        Args:
            text: 原始OCR识别文本
            
        Returns:
            str: 清理后的文本
        """
        if not text:
            return text
        
        # 处理数字1被误识别为/、\或l的情况
        cleaned_text = text
        import re
        
        # 定义需要替换的字符映射
        replacements = [
            # q被误识别为9的处理
            (r'^q-(\d+)-(\d+)$', r'9-\1-\2', 'q开头多级前缀'),
            (r'^q-(\d+)$', r'9-\1', 'q开头单级前缀'),
            (r'^(\d+)-q-(\d+)$', r'\1-9-\2', '中间q多级前缀'),
            (r'^(\d+)-q$', r'\1-9', '中间q单级前缀'),
            (r'^(\d+)-(\d+)-q$', r'\1-\2-9', '结尾q多级前缀'),
            (r'^(\d+)-(\d+)-(\d+)-q$', r'\1-\2-\3-9', '结尾q三级前缀'),
            
            # 双字符替换模式
            (r'(\d+)-/-/', r'\1-1-1', '双正斜杠'),
            (r'(\d+)-\\-\\', r'\1-1-1', '双反斜杠'),
            (r'(\d+)-l-l', r'\1-1-1', '双小写l'),
            (r'(\d+)-L-L', r'\1-1-1', '双大写L'),
            (r'(\d+)-/-l', r'\1-1-1', '正斜杠+小写l'),
            (r'(\d+)-l-/', r'\1-1-1', '小写l+正斜杠'),
            (r'(\d+)-\\-l', r'\1-1-1', '反斜杠+小写l'),
            (r'(\d+)-l-\\', r'\1-1-1', '小写l+反斜杠'),
            (r'(\d+)-/-\\', r'\1-1-1', '正斜杠+反斜杠'),
            (r'(\d+)-\\-/', r'\1-1-1', '反斜杠+正斜杠'),
            
            # 单字符替换模式
            (r'(\d+)-/', r'\1-1', '单正斜杠'),
            (r'(\d+)-\\', r'\1-1', '单反斜杠'),
            (r'(\d+)-l', r'\1-1', '单小写l'),
            (r'(\d+)-L', r'\1-1', '单大写L'),
            
            # 处理开头或结尾的误识别
            (r'^(\d+)/', r'\1-1', '开头正斜杠'),
            (r'^(\d+)\\\\', r'\1-1', '开头反斜杠'),
            (r'^(\d+)l', r'\1-1', '开头小写l'),
            (r'^(\d+)L', r'\1-1', '开头大写L'),
            
            # 处理连续误识别的情况
            (r'(\d+)/+', r'\1-1', '连续正斜杠'),
            (r'(\d+)\\\\+', r'\1-1', '连续反斜杠'),
            (r'(\d+)l+', r'\1-1', '连续小写l'),
            (r'(\d+)L+', r'\1-1', '连续大写L'),
            
            # 处理以1开头被误识别的情况
            (r'^l-(\d+)', r'1-\1', '开头l误识别'),
            (r'^L-(\d+)', r'1-\1', '开头L误识别'),
            (r'^/-(\d+)', r'1-\1', '开头/误识别'),
            (r'^\\-(\d+)', r'1-\1', '开头\\误识别'),
            
            # 处理以1开头且后面还有误识别的情况
            (r'^l-(\d+)-/', r'1-\1-1', '开头l+正斜杠'),
            (r'^l-(\d+)-\\', r'1-\1-1', '开头l+反斜杠'),
            (r'^l-(\d+)-l', r'1-\1-1', '开头l+小写l'),
            (r'^l-(\d+)-L', r'1-\1-1', '开头l+大写L'),
            (r'^L-(\d+)-/', r'1-\1-1', '开头L+正斜杠'),
            (r'^L-(\d+)-\\', r'1-\1-1', '开头L+反斜杠'),
            (r'^L-(\d+)-l', r'1-\1-1', '开头L+小写l'),
            (r'^L-(\d+)-L', r'1-\1-1', '开头L+大写L'),
            (r'^/-(\d+)-/', r'1-\1-1', '开头/+正斜杠'),
            (r'^/-(\d+)-\\', r'1-\1-1', '开头/+反斜杠'),
            (r'^/-(\d+)-l', r'1-\1-1', '开头/+小写l'),
            (r'^/-(\d+)-L', r'1-\1-1', '开头/+大写L'),
            (r'^\\-(\d+)-/', r'1-\1-1', '开头\\+正斜杠'),
            (r'^\\-(\d+)-\\', r'1-\1-1', '开头\\+反斜杠'),
            (r'^\\-(\d+)-l', r'1-\1-1', '开头\\+小写l'),
            (r'^\\-(\d+)-L', r'1-\1-1', '开头\\+大写L'),
        ]
        
        # 应用所有替换规则
        for pattern, replacement, description in replacements:
            if re.search(pattern, cleaned_text):
                old_text = cleaned_text
                cleaned_text = re.sub(pattern, replacement, cleaned_text)
                if old_text != cleaned_text:
                    print(f"文本清理: '{text}' -> '{cleaned_text}' ({description})")
        
        return cleaned_text
    
    def ocr_prefix_region(self, image: np.ndarray, page_num: int = 0) -> List[Dict]:
        """
        专门对左上角区域（宽度1/4，高度1/12）进行OCR识别，用于前缀识别
        
        Args:
            image: 原始页面图像
            page_num: 页面编号
            
        Returns:
            List[Dict]: 前缀区域的OCR识别结果
        """
        try:
            # 提取左上角区域（宽度1/4，高度1/12）
            prefix_region = self.extract_top_left_quarter_width_twelfth_height(image)
            
            # 保存切割的图像到本地
            self.save_cropped_image(prefix_region, page_num, "quarter_width_twelfth_height")
            
            # 对左上角区域进行OCR识别
            result = self.ocr.ocr(prefix_region, cls=True)
            
            if not result or not result[0]:
                return []
            
            prefix_results = []
            for line in result[0]:
                if line and len(line) >= 2:
                    # 提取文本和位置信息
                    bbox = line[0]  # 边界框坐标
                    text_info = line[1]  # (文本, 置信度)
                    
                    if not text_info or len(text_info) < 2:
                        continue
                    
                    # 计算文本区域的高度（考虑文字方向）
                    bbox_height = max(bbox[2][1] - bbox[0][1], bbox[3][1] - bbox[1][1])
                    bbox_width = max(bbox[1][0] - bbox[0][0], bbox[2][0] - bbox[3][0])
                    
                    # 如果宽度明显大于高度，说明是横向文字，使用高度
                    # 如果高度明显大于宽度，说明是竖向文字，使用宽度作为"有效高度"
                    if bbox_width > bbox_height * 1.5:  # 横向文字
                        height = bbox_height
                    elif bbox_height > bbox_width * 1.5:  # 竖向文字
                        height = bbox_width  # 用宽度作为有效高度
                    else:  # 接近正方形，可能是单个字符
                        height = min(bbox_height, bbox_width)
                    
                    # 清理OCR识别文本
                    original_text = text_info[0].strip()
                    cleaned_text = self.clean_ocr_text(original_text)
                    
                    prefix_results.append({
                        'text': cleaned_text,
                        'original_text': original_text,  # 保存原始文本用于调试
                        'confidence': text_info[1],
                        'bbox': bbox,
                        'height': height,
                        'width': bbox_width,
                        'bbox_height': bbox_height,  # 原始高度
                        'center_y': (bbox[0][1] + bbox[2][1]) / 2
                    })
            
            print(f"第 {page_num+1} 页左上角区域（宽度1/4，高度1/12）识别到 {len(prefix_results)} 个文本区域")
            
            # 保存策略2识别到的文本
            self.save_prefix_region_text(page_num, prefix_results, "strategy2")
            
            return prefix_results
            
        except Exception as e:
            print(f"警告：前缀区域OCR识别时出现错误: {e}")
            return []
    
    def find_level2_bookmark(self, page_content: Dict, prev_page_content: Optional[Dict] = None, prefix_ocr_results: List[Dict] = None) -> Optional[str]:
        """
        识别二级书签（表格中的主要字段或标题）
        
        Args:
            page_content: 当前页面内容
            prev_page_content: 上一页内容
            prefix_ocr_results: 左上角区域的OCR结果，用于前缀识别
            
        Returns:
            Optional[str]: 二级书签文本
        """
        ocr_results = page_content['ocr_results']
        if not ocr_results:
            return None
        
        # 查找前缀（如"2-1"、"3-2-1"等，至少包含一个连字符）
        prefix = None
        prefix_patterns = [
            r'^[\d]+(?:-[\d]+)+$',  # 多级数字格式：2-1, 3-2-1, 4-3-2-1 (至少一个连字符)
        ]
        
        # 前缀识别策略：先全图识别，没有则用1/4图识别
        print("开始前缀识别...")
        
        # 策略1：先使用全图顶部区域进行前缀识别
        print("策略1：使用全图顶部区域进行前缀识别")
        min_y = min(r['center_y'] for r in ocr_results)
        top_threshold = min_y + 500  # 允许100像素的误差
        
        print("全图顶部区域识别的文本内容：")
        top_results = [r for r in ocr_results if r['center_y'] <= top_threshold]
        for i, result in enumerate(top_results, 1):
            original_text = result.get('original_text', result['text'])
            if original_text != result['text']:
                print(f"  {i}. '{result['text']}' (原始: '{original_text}', 置信度: {result['confidence']:.3f}, Y坐标: {result['center_y']:.1f})")
            else:
                print(f"  {i}. '{result['text']}' (置信度: {result['confidence']:.3f}, Y坐标: {result['center_y']:.1f})")
        
        # 在全图顶部区域查找前缀
        for result in ocr_results:
            if result['center_y'] <= top_threshold:
                text = result['text'].strip()
                # 在前缀识别时，将'X'替换为'1'
                text_for_prefix = text.replace('X', '1')
                for pattern in prefix_patterns:
                    if re.match(pattern, text_for_prefix):
                        # 验证前缀是否与当前一级书签匹配
                        current_level1_index = self.get_level1_index(page_content)
                        if self.validate_prefix(text_for_prefix, current_level1_index):
                            prefix = text_for_prefix
                            print(f"✓ 在全图顶部区域找到有效前缀: '{prefix}' (原始: '{text}', 匹配模式: {pattern})")
                            break
                        else:
                            print(f"✗ 在全图顶部区域找到无效前缀: '{text_for_prefix}' (原始: '{text}'), 跳过")
                if prefix:
                    break
        
        # 策略2：如果全图没找到前缀，使用左上角区域（宽度1/4，高度1/12）进行前缀识别
        if not prefix and prefix_ocr_results:
            print("策略2：全图未找到前缀，使用左上角区域（宽度1/4，高度1/12）进行前缀识别")
            print(f"左上角区域共 {len(prefix_ocr_results)} 个文本区域")
            print("左上角区域识别的文本内容：")
            for i, result in enumerate(prefix_ocr_results, 1):
                original_text = result.get('original_text', result['text'])
                if original_text != result['text']:
                    print(f"  {i}. '{result['text']}' (原始: '{original_text}', 置信度: {result['confidence']:.3f})")
                else:
                    print(f"  {i}. '{result['text']}' (置信度: {result['confidence']:.3f})")
            
            for result in prefix_ocr_results:
                text = result['text'].strip()
                # 在前缀识别时，将'X'替换为'1'
                text_for_prefix = text.replace('X', '1')
                for pattern in prefix_patterns:
                    if re.match(pattern, text_for_prefix):
                        # 验证前缀是否与当前一级书签匹配
                        current_level1_index = self.get_level1_index(page_content)
                        if self.validate_prefix(text_for_prefix, current_level1_index):
                            prefix = text_for_prefix
                            print(f"✓ 在左上角区域找到有效前缀: '{prefix}' (原始: '{text}', 匹配模式: {pattern})")
                            break
                        else:
                            print(f"✗ 在左上角区域找到无效前缀: '{text_for_prefix}' (原始: '{text}'), 跳过")
                if prefix:
                    break
        
        if not prefix and not prefix_ocr_results:
            print("策略2：全图未找到前缀，且未提供左上角区域OCR结果")
        
        # 检查是否应该继承上一页的二级书签
        inherit_previous = False
        if not prefix and prev_page_content and 'level2_prefix' in prev_page_content and 'level2_title' in prev_page_content:
            # 如果当前页没有识别到前缀，且上一页有完整的二级书签信息
            inherit_previous = True
            prefix = prev_page_content['level2_prefix']
            selected_text = prev_page_content['level2_title']
            print(f"继承上一页信息：前缀='{prefix}', 标题='{selected_text}'")
        
        # 如果没有找到前缀且不继承，生成默认前缀
        if not prefix and not inherit_previous:
            # 获取当前一级书签的索引
            level1_index = self.get_level1_index(page_content)
            prefix = f"{level1_index}-1"
            print(f"使用默认前缀: '{prefix}'")
        
        # 保存当前页的前缀
        page_content['level2_prefix'] = prefix
        
        # 打印前缀识别结果总结
        if prefix:
            print(f"第 {page_content['page_num']+1} 页前缀识别成功: '{prefix}' (格式: X-X或更复杂)")
        else:
            print(f"第 {page_content['page_num']+1} 页未识别到前缀 (要求至少X-X格式)")
        
        # 只有在不继承上一页时才进行文本识别
        if not inherit_previous:
            # 计算页面顶部阈值（用于标题识别）- 只识别上半部分
            min_y = min(r['center_y'] for r in ocr_results)
            max_y = max(r['center_y'] for r in ocr_results)
            # 只对图片的上半部分进行识别
            top_threshold = min_y + (max_y - min_y) / 2  # 上半部分的中点
            
            # 计算页面宽度阈值 - 跳过左边X坐标大于二分之一宽度的文本块
            min_x = min(min(r['bbox'][0][0], r['bbox'][2][0]) for r in ocr_results)
            max_x = max(max(r['bbox'][1][0], r['bbox'][3][0]) for r in ocr_results)
            # 计算二分之一宽度
            half_width = min_x + (max_x - min_x) / 2  # 二分之一宽度点
            
            print(f"只对图片上半部分进行标题识别，跳过左边X坐标大于二分之一宽度的文本块 (Y坐标范围: {min_y:.1f} - {top_threshold:.1f}, 跳过X坐标 > {half_width:.1f} 的文本)")
            
            # 查找字体较大的文本（排除前缀后选择最大字体的）
            candidate_texts = []
            
            print("检查上半部分文本，排除前缀后选择最大字体的，跳过左边X坐标大于二分之一宽度的：")
            
            for result in ocr_results:
                # 计算文本块的左边X坐标
                left_x = min(result['bbox'][0][0], result['bbox'][2][0])
                
                if result['center_y'] <= top_threshold and left_x <= half_width:  # 只检查上半部分，且跳过左边X坐标大于二分之一宽度的
                    text = result['text'].strip()
                    height = result['height']
                    confidence = result['confidence']
                    text_type = result.get('text_type', 'printed')  # 获取文本类型
                    
                    # 排除前缀文本
                    is_prefix = text == prefix  # 明确排除已识别的前缀
                    is_handwritten = text_type == 'handwritten'  # 排除手写文本
                    high_confidence = confidence >= 0.9  # 置信度必须≥0.9
                    length_ok = len(text) > 2
                    not_pure_numbers = not re.match(r'^[\d\-\s\.、]+$', text)
                    has_chinese = re.search(r'[\u4e00-\u9fff]', text)
                    has_keyword = self.contains_keyword(text)  # 检查是否包含关键词
                    
                    type_label = '手写' if text_type == 'handwritten' else '印刷'
                    keyword_label = ' [关键词]' if has_keyword else ''
                    print(f"  '{text}' [{type_label}]{keyword_label} - 高度: {height:.1f}px, 置信度: {confidence:.3f}, 左边X坐标: {left_x:.1f}, 是前缀: {is_prefix}, 是手写: {is_handwritten}, 高置信度: {high_confidence}, 长度: {length_ok}, 非纯数字: {not_pure_numbers}, 包含中文: {has_chinese}, 包含关键词: {has_keyword}")
                    
                    if (not is_prefix and not is_handwritten and high_confidence and length_ok and not_pure_numbers and has_chinese):
                        candidate_texts.append((result, text))
                        print(f"    ✓ 添加到候选列表 (印刷+高置信度文本)")
                    else:
                        if is_handwritten:
                            print(f"    ✗ 被过滤条件排除 (手写文本)")
                        elif not high_confidence:
                            print(f"    ✗ 被过滤条件排除 (置信度{confidence:.3f} < 0.9)")
                        else:
                            print(f"    ✗ 被过滤条件排除")
                elif result['center_y'] <= top_threshold:
                    print(f"  '{result['text']}' - 跳过，左边X坐标 {left_x:.1f} > {half_width:.1f}")
            
            # 选择文本：优先选择包含关键词且是前五个的文本
            selected_text = None
            if candidate_texts:
                # 检查前五个候选文本中是否有包含关键词的
                keyword_candidates = []
                for i, (result, text) in enumerate(candidate_texts[:5]):  # 只检查前五个
                    if self.contains_keyword(text):
                        keyword_candidates.append((result, text, i))
                        print(f"  🎯 发现关键词文本: '{text}' (位置: 第{i+1}个)")
                
                if keyword_candidates:
                    # 如果有包含关键词的文本，选择第一个（位置最靠前）
                    selected_result, selected_text, position = keyword_candidates[0]
                    print(f"✓ 优先选择关键词文本: '{selected_text}' (位置: 第{position+1}个, 高度: {selected_result['height']:.1f}px)")
                else:
                    # 如果没有包含关键词的文本，选择最大字体的
                    selected_text = max(candidate_texts, key=lambda x: x[0]['height'])[1]
                    max_height = max(candidate_texts, key=lambda x: x[0]['height'])[0]['height']
                    print(f"✓ 选择最大字体文本: '{selected_text}' (高度: {max_height:.1f}px)")
            
            if not selected_text:
                return None
            
            # 清理文本
            selected_text = selected_text.strip()
            if not selected_text:
                return None
        
        # 保存当前页的标题
        page_content['level2_title'] = selected_text
        
        # 生成后缀
        # 检查是否是一级书签后的第一个二级书签或识别到了前缀
        is_first_level2_after_level1 = False
        has_detected_prefix = prefix is not None and not inherit_previous
        
        # 如果识别到了前缀，说明这是一个新的二级书签序列
        if has_detected_prefix:
            is_first_level2_after_level1 = True
        # 如果上一页是一级书签，当前页是第一个二级书签
        elif prev_page_content and 'is_level1_page' in prev_page_content and prev_page_content['is_level1_page']:
            is_first_level2_after_level1 = True
        
        if is_first_level2_after_level1 or not (prev_page_content and 'level2_suffix' in prev_page_content):
            suffix_num = 1
            print(f"使用初始后缀: {suffix_num:02d}")
        else:
            suffix_num = prev_page_content['level2_suffix'] + 1
            print(f"继承上一页后缀并递增: {prev_page_content['level2_suffix']} -> {suffix_num:02d}")
        
        # 构建二级书签
        if prefix:
            bookmark = f"{prefix} {selected_text}{suffix_num:02d}"
        else:
            bookmark = f"{selected_text}{suffix_num:02d}"
        
        # 保存后缀到页面内容中
        page_content['level2_suffix'] = suffix_num
        
        # 打印最终的书签生成结果
        if inherit_previous:
            print(f"最终二级书签: '{bookmark}' (继承上一页: 前缀='{prefix}', 标题='{selected_text}', 后缀={suffix_num:02d})")
        else:
            print(f"最终二级书签: '{bookmark}' (新识别: 前缀='{prefix}', 标题='{selected_text}', 后缀={suffix_num:02d})")
        
        return bookmark
    
    def generate_bookmarks(self):
        """生成书签结构，确保每一页都有唯一的一级或二级书签"""
        print("开始生成书签...")
        
        current_level1 = None
        level2_counter = 0
        
        for i, page_content in enumerate(self.pages_content):
            page_num = page_content['page_num']
            has_bookmark = False
            print(f"处理第 {i+1} 页，page_num: {page_num}")
            
            # 检查一级书签
            level1_bookmark = self.find_level1_bookmark(page_content)
            if level1_bookmark:
                current_level1 = level1_bookmark
                level2_counter = 0
                # 标记当前页面为一级书签页面
                page_content['is_level1_page'] = True
                self.bookmarks.append({
                    'title': level1_bookmark,
                    'page': page_num,
                    'level': 1,
                    'parent': None
                })
                print(f"第 {page_num+1} 页发现一级书签: {level1_bookmark} (页码: {page_num})")
                has_bookmark = True
                # 发现一级书签后直接跳到下一页，不再处理二级书签
                continue
            
            # 检查二级书签
            if current_level1:  # 只有在有一级书签的情况下才生成二级书签
                prev_page = self.pages_content[i-1] if i > 0 else None
                prefix_ocr_results = page_content.get('prefix_ocr_results', [])
                level2_bookmark = self.find_level2_bookmark(page_content, prev_page, prefix_ocr_results)
                
                if level2_bookmark:
                    level2_counter += 1
                    self.bookmarks.append({
                        'title': level2_bookmark,
                        'page': page_num,
                        'level': 2,
                        'parent': current_level1
                    })
                    print(f"第 {page_num+1} 页发现二级书签: {level2_bookmark} (页码: {page_num})")
                    has_bookmark = True
            
            # 如果当前页没有书签，创建一个默认的二级书签
            if not has_bookmark and current_level1:
                level2_counter += 1
                # 获取本页字体最大的文本作为标题
                largest_text = self.get_largest_text_from_page(page_content)
                if largest_text:
                    # 获取前缀
                    prefix = page_content.get('level2_prefix', f"{self.get_level1_index(page_content)}-1")
                    default_bookmark = f"{prefix} {largest_text}{level2_counter:02d}"
                else:
                    default_bookmark = f"第{page_num+1}页{level2_counter:02d}"
                
                self.bookmarks.append({
                    'title': default_bookmark,
                    'page': page_num,
                    'level': 2,
                    'parent': current_level1
                })
                print(f"第 {page_num+1} 页创建默认二级书签: {default_bookmark} (页码: {page_num})")
                has_bookmark = True
            
            # 如果当前页没有书签且没有一级书签，创建一个默认的一级书签
            if not has_bookmark:
                current_level1 = f"第{page_num+1}页内容"
                level2_counter = 0
                self.bookmarks.append({
                    'title': current_level1,
                    'page': page_num,
                    'level': 1,
                    'parent': None
                })
                print(f"第 {page_num+1} 页创建默认一级书签: {current_level1} (页码: {page_num})")
                has_bookmark = True
    
    def create_bookmarked_pdf(self, output_path: str):
        """
        创建带书签的PDF
        
        Args:
            output_path: 输出PDF路径
        """
        print("开始创建带书签的PDF...")
        
        # 创建新的PDF文档
        new_doc = fitz.open()
        
        # 复制所有页面 - 一次性复制所有页面保持页码一致性
        new_doc.insert_pdf(self.doc)
        
        # 构建书签结构
        toc = []
        level1_bookmarks = {}
        
        # 先处理一级书签
        for bookmark in self.bookmarks:
            if bookmark['level'] == 1:
                level1_bookmarks[bookmark['title']] = {
                    'title': bookmark['title'],
                    'page': bookmark['page'] + 1,
                    'children': []
                }
        
        # 再处理二级书签
        for bookmark in self.bookmarks:
            if bookmark['level'] == 2 and bookmark['parent'] in level1_bookmarks:
                level1_bookmarks[bookmark['parent']]['children'].append({
                    'title': bookmark['title'],
                    'page': bookmark['page'] + 1
                })
        
        # 构建最终的TOC结构
        for level1 in level1_bookmarks.values():
            # 一级书签格式: (level, title, page) - 页码从0开始
            toc_entry = (1, level1['title'], level1['page'])
            toc.append(toc_entry)
            
            # 添加二级书签
            for child in level1['children']:
                # 二级书签格式: (level, title, page) - 页码从0开始
                child_entry = (2, child['title'], child['page'])
                toc.append(child_entry)
        
        # 设置书签
        if toc:
            new_doc.set_toc(toc)
        
        # 保存PDF
        new_doc.save(output_path)
        new_doc.close()
        
        print(f"带书签的PDF已保存到: {output_path}")
    
    def process(self, output_path: str = None):
        """
        处理PDF并生成带书签的版本
        
        Args:
            output_path: 输出文件路径，默认为bookmark目录下的原文件名
        """
        if output_path is None:
            # 获取原文件名（不包含路径）
            original_filename = os.path.basename(self.pdf_path)
            output_path = os.path.join(self.config_output_dir, original_filename)
        
        try:
            # 处理所有页面
            self.process_all_pages()
            
            # 生成书签
            self.generate_bookmarks()
            
            # 保存所有文本内容
            self.save_all_texts()
            
            # 保存书签信息
            self.save_bookmarks()
            
            # 生成XLSX表格
            self.generate_xlsx_table()
            
            # 创建带书签的PDF
            self.create_bookmarked_pdf(output_path)
            
            print("PDF处理完成！")
            print(f"共识别到 {len([b for b in self.bookmarks if b['level'] == 1])} 个一级书签")
            print(f"共识别到 {len([b for b in self.bookmarks if b['level'] == 2])} 个二级书签")
            print(f"所有中间产物已保存到: {self.output_dir}")
            
        except Exception as e:
            print(f"错误：处理过程中出现错误: {e}")
            raise
        finally:
            self.doc.close()

class BatchPDFProcessor:
    """批量PDF处理器"""
    
    def __init__(self, config_file: str = "config.ini"):
        """
        初始化批量处理器
        
        Args:
            config_file: 配置文件路径
        """
        self.config_file = config_file
        self.config = configparser.ConfigParser()
        self.load_config()
        
        # 创建输出目录
        if not os.path.exists(self.output_dir):
            os.makedirs(self.output_dir)
            print(f"创建输出目录: {self.output_dir}")
    
    def load_config(self):
        """加载配置文件"""
        if not os.path.exists(self.config_file):
            self.create_default_config()
        
        self.config.read(self.config_file, encoding='utf-8')
        
        # 读取配置
        self.input_dir = self.config.get('paths', 'input_dir')
        self.output_dir = self.config.get('paths', 'output_dir')
        
        # 设置默认值
        self.max_workers = 5  # 默认5个线程
        self.save_intermediate = False  # 默认保存中间文件
        
        print(f"配置加载完成:")
        print(f"  输入目录: {self.input_dir}")
        print(f"  输出目录: {self.output_dir}")
        print(f"  最大线程数: {self.max_workers} (默认)")
        print(f"  保存中间文件: {self.save_intermediate} (默认)")
    
    def create_default_config(self):
        """创建默认配置文件"""
        self.config['paths'] = {
            'input_dir': './input_pdfs',
            'output_dir': './output_pdfs'
        }
        
        with open(self.config_file, 'w', encoding='utf-8') as f:
            self.config.write(f)
        
        print(f"已创建默认配置文件: {self.config_file}")
        print("请修改配置文件中的路径设置，然后重新运行程序")
    
    def get_pdf_files(self) -> List[str]:
        """获取输入目录中的所有PDF文件"""
        if not os.path.exists(self.input_dir):
            print(f"错误：输入目录不存在: {self.input_dir}")
            return []
        
        pdf_files = []
        for file in os.listdir(self.input_dir):
            if file.lower().endswith('.pdf'):
                pdf_files.append(os.path.join(self.input_dir, file))
        
        print(f"找到 {len(pdf_files)} 个PDF文件")
        return pdf_files
    
    def process_single_pdf(self, pdf_path: str) -> Dict:
        """处理单个PDF文件"""
        result = {
            'file': pdf_path,
            'success': False,
            'error': None,
            'start_time': time.time(),
            'end_time': None
        }
        
        try:
            print(f"开始处理: {os.path.basename(pdf_path)}")
            
            # 创建处理器实例
            processor = PDFBookmarkProcessor(pdf_path, self.output_dir)
            
            # 设置输出路径
            base_name = os.path.splitext(os.path.basename(pdf_path))[0]
            output_path = os.path.join(self.output_dir, f"{base_name}.pdf")
            
            # 处理PDF
            processor.process(output_path)
            
            result['success'] = True
            result['output_file'] = output_path
            print(f"✓ 完成处理: {os.path.basename(pdf_path)}")
            
        except Exception as e:
            result['error'] = str(e)
            print(f"✗ 处理失败: {os.path.basename(pdf_path)} - {e}")
        
        finally:
            result['end_time'] = time.time()
            result['duration'] = result['end_time'] - result['start_time']
        
        return result
    
    def process_all(self):
        """批量处理所有PDF文件"""
        pdf_files = self.get_pdf_files()
        
        if not pdf_files:
            print("没有找到PDF文件需要处理")
            return
        
        print(f"开始批量处理 {len(pdf_files)} 个PDF文件，使用 {self.max_workers} 个线程")
        
        results = []
        start_time = time.time()
        
        # 使用线程池处理
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            # 提交所有任务
            future_to_pdf = {
                executor.submit(self.process_single_pdf, pdf_file): pdf_file 
                for pdf_file in pdf_files
            }
            
            # 收集结果
            for future in as_completed(future_to_pdf):
                pdf_file = future_to_pdf[future]
                try:
                    result = future.result()
                    results.append(result)
                except Exception as e:
                    print(f"✗ 线程执行异常: {os.path.basename(pdf_file)} - {e}")
                    results.append({
                        'file': pdf_file,
                        'success': False,
                        'error': str(e),
                        'start_time': time.time(),
                        'end_time': time.time(),
                        'duration': 0
                    })
        
        # 统计结果
        total_time = time.time() - start_time
        success_count = sum(1 for r in results if r['success'])
        failed_count = len(results) - success_count
        
        print("\n" + "="*60)
        print("批量处理完成!")
        print(f"总文件数: {len(results)}")
        print(f"成功处理: {success_count}")
        print(f"处理失败: {failed_count}")
        print(f"总耗时: {total_time:.2f} 秒")
        print(f"平均每个文件: {total_time/len(results):.2f} 秒")
        
        # 显示失败的文件
        if failed_count > 0:
            print("\n失败的文件:")
            for result in results:
                if not result['success']:
                    print(f"  - {os.path.basename(result['file'])}: {result['error']}")
        
        # 保存处理报告
        self.save_processing_report(results, total_time)
    
    def save_processing_report(self, results: List[Dict], total_time: float):
        """保存处理报告"""
        try:
            report_file = os.path.join(self.output_dir, "processing_report.txt")
            
            with open(report_file, 'w', encoding='utf-8') as f:
                f.write("PDF批量处理报告\n")
                f.write("=" * 50 + "\n")
                f.write(f"处理时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"总文件数: {len(results)}\n")
                f.write(f"成功处理: {sum(1 for r in results if r['success'])}\n")
                f.write(f"处理失败: {sum(1 for r in results if not r['success'])}\n")
                f.write(f"总耗时: {total_time:.2f} 秒\n")
                f.write(f"平均每个文件: {total_time/len(results):.2f} 秒\n\n")
                
                f.write("详细结果:\n")
                f.write("-" * 30 + "\n")
                
                for i, result in enumerate(results, 1):
                    f.write(f"{i:3d}. {os.path.basename(result['file'])}\n")
                    f.write(f"     状态: {'成功' if result['success'] else '失败'}\n")
                    f.write(f"     耗时: {result['duration']:.2f} 秒\n")
                    if result['success']:
                        f.write(f"     输出: {os.path.basename(result['output_file'])}\n")
                    else:
                        f.write(f"     错误: {result['error']}\n")
                    f.write("\n")
            
            print(f"处理报告已保存: {report_file}")
            
        except Exception as e:
            print(f"保存处理报告失败: {e}")

def main():
    """主函数"""
    # 检查是否有命令行参数
    import sys
    
    if len(sys.argv) > 1:
        # 单文件处理模式
        pdf_path = sys.argv[1]
        if not os.path.exists(pdf_path):
            print(f"错误：PDF文件不存在: {pdf_path}")
            return
        
        print(f"单文件处理模式: {pdf_path}")
        processor = PDFBookmarkProcessor(pdf_path)
        processor.process()
    else:
        # 批量处理模式
        print("批量处理模式")
        batch_processor = BatchPDFProcessor()
        batch_processor.process_all()

if __name__ == "__main__":
    main()
